package socket;

import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
import java.nio.charset.StandardCharsets;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**
 * 聊天室服务端
 */
public class Server {
    private ServerSocket serverSocket;

    private ExecutorService threadPool;

    //存放所有客户端的输出流,用于广播消息,名字作为key,输出流作为value
    private Map<String,PrintWriter> allOut = new HashMap<>();
    public Server(){
        try {
            System.out.println("正在启动服务端");
            serverSocket = new ServerSocket(8088);
            threadPool = Executors.newFixedThreadPool(30);
            System.out.println("服务端启动完毕");
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }
    public void start(){
        try {
            while (true){
                System.out.println("等待客户端链接...");
                Socket socket = serverSocket.accept();
                System.out.println("一个客户端链接了");

                //实例化线程任务
                ClientHandler handler = new ClientHandler(socket);
                //实例化线程
//                Thread t = new Thread(handler);
                //启动线程
//                t.start();
                threadPool.execute(handler);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public static void main(String[] args) {

        Server server = new Server();
        server.start();
    }
    private class ClientHandler implements Runnable{
        private Socket socket;
        private String ip;
        private String nickname;
        public ClientHandler(Socket socket){
            this.socket = socket;
            ip = socket.getInetAddress().getHostAddress();
        }
        public void run(){
            PrintWriter pw = null;
            try {
                InputStream in = socket.getInputStream();
                InputStreamReader isr = new InputStreamReader(in, StandardCharsets.UTF_8);
                BufferedReader br = new BufferedReader(isr);

                //通过socket获取输出流并流链接为PrintWriter,为了给客户端发送消息
                OutputStream out = socket.getOutputStream();
                OutputStreamWriter osw = new OutputStreamWriter(out,StandardCharsets.UTF_8);
                BufferedWriter bw = new BufferedWriter(osw);
                pw = new PrintWriter(bw,true);

                //首先单独读取一行字符串,客户端发送过来的第一行字符串应当是昵称
                nickname = br.readLine();

                //将该客户的输出流存入共享集合中
                synchronized (allOut){
                    allOut.put(nickname,pw);
                }
                sendMessage(nickname+"上线了,当前在线人数:"+allOut.size());

                String message;
                while((message=br.readLine())!=null){
                    //聊天信息以"@"开始,应当是私聊
                    if (message.startsWith("@")){
                         sendMessageToSomeOne(message);
                    }else{
                        sendMessage(nickname+"["+ip+"]说:"+message);
                        //将群聊消息保存到数据库
                        saveMessage(nickname,"[所有人]",message);
                    }
                }
            } catch (IOException e) {
                System.out.println(e);
            }finally{
                //处理客户端断开连接后的操作
                //将该客户的输出流从共享集合allOut中删除
                synchronized (allOut){
                    allOut.remove(nickname);
                }
                sendMessage(nickname+"下线了,当前在线人数:"+allOut.size());
                //将socket关闭,释放底层资源
                try {
                    socket.close();
                } catch (IOException e) {
                    throw new RuntimeException(e);
                }
            }

        }
        /*
            将消息发送给所有客户端
         */
        public void sendMessage(String message){
            System.out.println(message);//先在服务器控制台上输出一下
            //遍历要和增删互斥,迭代器要求遍历过程中不可以通过集合方法增删元素
            synchronized (allOut){
                for (PrintWriter o : allOut.values()){
                    o.println(message);
                }
            }
        }
        /**
         * 将消息发送给指定用户(私聊)
         * @parm message 格式:@对方名称:聊天信息
         */
        private void sendMessageToSomeOne(String message){
            String regx ="@.+:.+";
            if (message.matches(regx)){
                //根据聊天消息，截取出对方的昵称
                String toNickname = message.substring(1,message.indexOf(":"));
                if (allOut.containsKey(toNickname)){//判断昵称是否存在
                    //获取对方的输出流
                    PrintWriter pw = allOut.get(toNickname);
                    String content = message.substring(message.indexOf(":")+1);
                    pw.println(nickname+"悄悄对你说:"+content);//将私聊消息发送给对方
                    //将私聊消息保存到数据库中
                    saveMessage(nickname,toNickname,content);

                }else{
                    PrintWriter pw =allOut.get(nickname);
                    pw.println("用户["+toNickname+"]不存在");
                }
            }else{//格式不对,通知客户端
                PrintWriter pw =allOut.get(nickname);
                pw.println("私聊格式不对,应当是@对方昵称:聊天消息");
            }
        }

        private void saveMessage(String fromUSer,String toUser,String content){
            try (
                    Connection conn = DBUtil.getConnection();
                    ){
                String sql="INSERT INTO chatinfo(content,from_user,to_user) "+
                        "VALUES(?,?,?)";
                PreparedStatement ps = conn.prepareStatement(sql);
                ps.setString(1,content);
                ps.setString(2,fromUSer);
                ps.setString(3,toUser);
                ps.executeUpdate();
            } catch (SQLException e) {
                throw new RuntimeException(e);
            }

        }
    }

}
